# -*- coding: utf-8 -*-
import re
import random
from builtins import range

#Abrimos el fichero
f=open("fichero.arff")

#Recorremos todo el fichero y lo almacenamos en "s"
s=""
for line in f:
    if not re.match("^%",line) and not re.match("^\s",line):
        s = s+line
    
f.close()

#Almacenamos en una lista los atributos(posiciones) con sus valores :
a = s.splitlines()
#print (a)
nAtributos=0
nValoresDeCadaAtributo = []
lineasAtributos = []
for palabra in a:
    if re.match("^@attribute",palabra):
        lineasAtributos.append(palabra)
        nAtributos+=1
        nValoresDeCadaAtributo.append(palabra.count(',')+1)
#print("Estos son los atributos :", lineasAtributos)  

#Le quitamos el ultimo atributo ya que consideramos la hipotesis del mundo cerrado y por lo tanto el atributo del "entonces" no es necesario almacenarlo      
nAtributos-=1
nValoresDeCadaAtributo.pop()

# Esto tenemos que ponerlo con un try cath para que no continue la ejecucion del programa
if nValoresDeCadaAtributo[len(nValoresDeCadaAtributo)-1] != 2:
    print("ERROR !!!, fichero arff no valido")

recortados = []
for atributo in lineasAtributos:
        primeraLlave = atributo.index("{")
        segundaLlave = atributo.index("}")
        recortados.append(atributo[primeraLlave+1:segundaLlave])
#recortados.pop(len(recortados)-1)
#print("Estos son los atributos recortados :", recortados)

divididos = []
for recortado in recortados:
    divididos.append(recortado.split(","))

listaDeAtributos = []
for dividido in divididos:
    subdefinitivo = []
    for subdividido in dividido: 
        subdefinitivo.append(subdividido.strip())
    listaDeAtributos.append(subdefinitivo)
    
print("Lista de atributos con sus valores: ", listaDeAtributos)


#Calculamos la longitud de cada regla
longRegla = 0
for digito in nValoresDeCadaAtributo:
    longRegla = longRegla + int(digito)

print ("El numero de atributos es : ", nAtributos)   
print("El numero de valores de cada atributo es: ", nValoresDeCadaAtributo)
print ("Por lo tanto la longitud de cada regla es : ", longRegla)


#Almacenamos en una lista el conjunto de entranamiento (@Data) :

for linea in a:
    if linea == "@data":
        indiceData = a.index("@data") 

conjuntoDeEntrenamiento = []
for data in a[indiceData+1:len(a)]:
    conjuntoDeEntrenamiento.append(data)
  
#print("Este es el conjuntoDeEntrenamiento: ",conjuntoDeEntrenamiento)  

conjuntoDeEntrenamientoFinal = []
for conj in conjuntoDeEntrenamiento:
    conjuntoDeEntrenamientoFinal.append(conj.split(","))

print("Este es el conjuntoDeEntrenamiento: ",conjuntoDeEntrenamientoFinal)



#Pasamos a binario el conjunto de entrenamiento :

# reglaTodoACero = ""
# for i in range(longRegla):
#     reglaTodoACero += "0";
    

conjuntoDeEntrenamientoBinario = []
for linea in conjuntoDeEntrenamientoFinal:
    nueva = ""
    puntero = 0
    pos = 0
    for sublinea in linea:
        if not linea.index(sublinea) == len(linea)-1:
            #Solventamos el problema del '?' en el conjunto de entrenamiento
            if sublinea == '?':
                posicion = -1
            else:
                posicion = listaDeAtributos[linea.index(sublinea)].index(sublinea)
            #print(posicion)
            if posicion == -1:
                nueva += "0"*nValoresDeCadaAtributo[pos]
            elif puntero+posicion-1 < puntero:
                nueva += "1"+"0"*(nValoresDeCadaAtributo[pos]-1)
                #print(nueva)
            else:
                nueva += "0"*posicion
                nueva += "1"
                nueva += "0"*(nValoresDeCadaAtributo[pos]-posicion-1)
            #print(nueva)
            puntero += nValoresDeCadaAtributo[pos]
            pos+=1
            
        else:
            #print("estamos en la class")
            posicion = listaDeAtributos[linea.index(sublinea)].index(sublinea)
            #print(posicion)
            if posicion == 0:
                nueva += "0"
            else:
                nueva += "1"
                
        
    conjuntoDeEntrenamientoBinario.append(nueva)
print("Conjunto de entrenamiento binario : ",conjuntoDeEntrenamientoBinario)
     

  
# # Creacion de la clase individuo
# class individuo:
#     fitness = 0.0
#     reglas = []
    



#Metodos auxiliares

#Metodo que genera una regla al azar 
def generaReglaAlAzar():

    reglaGenerada = ""
 
    for _ in range(longRegla):
        reglaGenerada += str(random.randrange(0,2))
        
    return reglaGenerada

#print(generaReglaAlAzar());


def encontrarElementoEnLista(elemento,lista):
        try:
            index_element=lista.index(elemento)
            return index_element
        except ValueError:
            return -1



#Pseudocodigo !!!

# INICIAR población
# EVALUAR cada individuo de la población

# Repetir hasta CONDICIÓN_DE_TERMINACIÓN
    # SELECCIONAR padres
    # COMBINAR pares de padres
    # MUTAR hijos resultantes
    # EVALUAR nuevos individuos
    # SELECCIONAR individuos para la siguiente generación

# Devolver el mejor de la última generación


# N cromosomas, K maximo nº de reglas en cada cromosoma, T iteraciones

N = 20
K= 20


#Iniciar poblacion :

def generaPoblacionInicial (N , K):

    poblacion = []


    for i in range(N):
        cromosoma = []
        nReglas = random.randrange(1,K+1) #Cada uno de los cromosomas tendra este numero aleatorio de reglas.(OJO: cada cromosoma tendra un  numero de reglas distinto).
        #print(nReglas)
        for j in range(nReglas):
            cromosoma.append(generaReglaAlAzar())
        poblacion.append(cromosoma)
    return poblacion

poblacionInicial = generaPoblacionInicial(N,K)
print("La poblacion inicial es: ",poblacionInicial)


#Evaluar cada individuo de la poblacion
# 
# def evaluarCadaIndividuoDeLaPoblacion (poblacion2, conjuntoDeEntrenamientoBinario):
#     
#     #En posicionesCromosoma guardaremos las posiciones donde hay un "1" por cada uno de los atributos de cada regla.
#     posicionesCromosoma = []
#     for cromosoma in poblacion2:
#         prueba = []
#         for regla in cromosoma:
#             posicionesRegla = []
#             indice = -1
#             posiciones = []
#             pos = 0
#             indiceSegundo = 0
#             for i in regla:
#                 indice += 1
#                 indiceSegundo += 1
#                 if i=="1":
#                     posiciones.append(indice)
#                 if indiceSegundo == nValoresDeCadaAtributo[pos]:
#                     indiceSegundo = 0
#                     posicionesRegla.append(posiciones)
#                     posiciones = []
#                     pos += 1
#             prueba.append(posicionesRegla)
#             
#         posicionesCromosoma.append(prueba)
#     
#     print("posicionesCromosoma", posicionesCromosoma)
#     
#     
#     
#     
#     #Ahora hacemos lo mismo con el conjunto de entrenamiento para ver cual es la posicion del valor de cada
#     #uno de sus atributos :
#     
#     prueba2 = []
#     for lineaEntrenamiento in conjuntoDeEntrenamientoBinario:
#             posicionesRegla2 = []
#             indice2 = -1
#             posiciones2 = []
#             pos2 = 0
#             indiceSegundo2 = 0
#             for i in lineaEntrenamiento[0:len(lineaEntrenamiento)-1]:
#                 indice2 += 1
#                 indiceSegundo2 += 1
#                 if i=="1":
#                     posiciones2.append(indice2)
#                 if indiceSegundo2 == nValoresDeCadaAtributo[pos2]:
#                     indiceSegundo2 = 0
#                     posicionesRegla2.append(posiciones2)
#                     posiciones2 = []
#                     pos2 += 1
#             prueba2.append(posicionesRegla2)
#     print("posiciones conjunto de entrenamiento: ", prueba2)
#         
# 
# 
#     #Ahora vemos por cada regla que porcentaje del conjunto de entrenamiento satisface:
#     satisfacionesPorPoblacion = []
#     for cromosoma in posicionesCromosoma:
#         numeroDeLineasDeEntrenamientoQueSatisfaceElCromosoma = 0
#         satisfacionesPorCromosoma = []
#         for regla in cromosoma:
#             numeroDeLineasDeEntrenamientoQueSatisfaceLaRegla = 0
#             ind = -1
#             for lineaEntrenamiento in prueba2:
#                 ind += 1
#                 indice = -1
#                 loSatisface = True
#                 #print("conjuntoDeEntrenamientoBinario[ind][longRegla]", conjuntoDeEntrenamientoBinario[ind][longRegla])
#                 for pos in lineaEntrenamiento:
#                     indice += 1
#                     if len(regla[indice]) != 0  and encontrarElementoEnLista(pos[0],regla[indice]) != -1 and conjuntoDeEntrenamientoBinario[ind][longRegla] == "1":
#                         loSatisface = True
#                         continue
#                     elif len(regla[indice]) != 0  and encontrarElementoEnLista(pos[0],regla[indice]) != -1 and conjuntoDeEntrenamientoBinario[ind][longRegla] == "0":
#                         loSatisface = False
#                         numeroDeLineasDeEntrenamientoQueSatisfaceLaRegla -= 1
#                         break
#                     elif len(regla[indice]) == 0:
#                         loSatisface = True
#                         continue
#                     elif len(regla[indice]) != 0 and encontrarElementoEnLista(pos[0],regla[indice]) == -1 and conjuntoDeEntrenamientoBinario[ind][longRegla] == "0":
#                         loSatisface = True
#                         continue
#                     elif len(regla[indice]) != 0 and encontrarElementoEnLista(pos[0],regla[indice]) == -1 and conjuntoDeEntrenamientoBinario[ind][longRegla] == "1":
#                         loSatisface = False
#                         numeroDeLineasDeEntrenamientoQueSatisfaceLaRegla -= 1
#                         break
#                     else:
# #                         loSatisface = False
# #                         numeroDeLineasDeEntrenamientoQueSatisfaceLaRegla -= 1
# #                         break
#                         print("error !!")
#                 numeroDeLineasDeEntrenamientoQueSatisfaceLaRegla += 1
#                 #print("la satisface ??", loSatisface)
#             #print("numeroDeLineasDeEntrenamientoQueSatisfaceLaRegla :", numeroDeLineasDeEntrenamientoQueSatisfaceLaRegla )
#             satisfacionesPorCromosoma.append(numeroDeLineasDeEntrenamientoQueSatisfaceLaRegla)
#             #print("satisfacionesPorCromosoma: ", satisfacionesPorCromosoma)
#         satisfacionesPorPoblacion.append(satisfacionesPorCromosoma)
#     print("satisfacionesPorPoblacion", satisfacionesPorPoblacion)
#                 
#     
#         
#                 
# print("metodo fitness: ", evaluarCadaIndividuoDeLaPoblacion(poblacionInicial, conjuntoDeEntrenamientoBinario))
#                      



#Combinar pares de padres

padre1 = ['0110000110', '0001110001', '0000001100'] 
padre2 = ['0010010001', '1010001001']

# padre1 = ['holaadios1', 'feomalotio', 'mongolotia'] 
# padre2 = ['maricontat', 'preciosoes']


def combinarParesPadres (padre1, padre2):
    
    padre1Plano =""
    for i in range(len(padre1)):
        padre1Plano += padre1[i]
    #print("este es el papa1 aplanao ",padre1Plano)

    padre2Plano =""
    for i in range(len(padre2)):
        padre2Plano += padre2[i]
    #print("este es el papa2 aplanao ",padre2Plano)
     
#     hijo1 = []
    hijo1Plano=""
#     hijo2 = []
    hijo2Plano=""
     
# #     Primero genero un numero aleatorio entre 0 y tamaño del padre 1 para ver en que regla hago el primer corte
    regla1P1 = random.randrange(0, len(padre1))
#     Luego otro numero aleatorio para ver en que regla eligo el segundo corte (tiene que ser mayor o igual (no puede salir en las reglas anteriores))
    regla2P1 = random.randrange(regla1P1, len(padre1)) 
#     Otro numero aleatorio para ver en que posicion de la regla 1 (elegida del primer random)  vamos a cortar
    
    posRegla1 = random.randrange(0, longRegla-1)
    
#     Otro numero aleatorio para ver en que posicion de la regla 2 (elegida del segundo random)  vamos a cortar
    

    posRegla2 = random.randrange(posRegla1+1, longRegla)
    
   
#     -------------------------
    
#     Primero genero un numero aleatorio entre 0 y tamaño del padre 2 para ver en que regla hago el primer corte
    regla1P2 = random.randrange(0, len(padre2))
#     Luego otro numero aleatorio para ver en que regla eligo el segundo corte (tiene que ser mayor o igual (no puede salir en las reglas anteriores))
    regla2P2 = random.randrange(regla1P2, len(padre2)) 
#     las posiciones seran las mismas que la del padre 1

# Primer corte en padre 1 definitivo
    corte1P1 = regla1P1*longRegla +posRegla1  
# Segundo corte en padre 1 definitivo
    corte2P1 = regla2P1*longRegla+posRegla2
# Primer corte en padre 2 definitivo
    corte1P2 = regla1P2*longRegla +posRegla1  
# Segundo corte en padre 2 definitivo
    corte2P2 = regla2P2*longRegla+posRegla2
   
#     print("lalalalalalalallalalal")
#     print("Esta es la regla1P1",regla1P1)
#     print("Esta es la regla2P1 ",regla2P1)
#     print("Posicion de corte regla1 ",posRegla1)
#     print("Posicion de corte regla2 ",posRegla2)
#     print("Esta es la regla1P2 ",regla1P2)
#     print("Esta es la regla2P2 ",regla2P2)
#     print("Definitivo c1p1 ",corte1P1)
#     print("Definitivo c2p1 ",corte2P1)
#     print("Definitivo c1p2 ",corte1P2)
#     print("Definitivo c2p2 ",corte2P2)
    
# vamos recorriendo el padre 1 y vamos agregando al hijo 1 todo hasta que llegamos a la
# regla1P1 y la posRegla1, entonces vamos a regla1P2, posRegla1 y copiamos todo hasta que
# lleguemos a regla2P2, posRegla2 y nos vamos a regla2P1, posRegla2 y terminamos de copiar

# //Con planos. 
# Vamos a ir recorriendo el padre1plano hasta que llega al corte1P1, entonces copiamos a partir 
# del corte1P2 hasta que llegamos al corte2P2 y cambiamos a corte2P1 y terminamos de copiar.



    for unidadP1H1 in padre1Plano[0:corte1P1]:
        hijo1Plano += unidadP1H1
    
    for unidadP2H1 in padre2Plano[corte1P2:corte2P2+1]:
        hijo1Plano += unidadP2H1

    for unidadP1AH1 in padre1Plano[corte2P1+1: len(padre1Plano)]:
        #print("unidadP1A",unidadP1AH1)
        hijo1Plano += unidadP1AH1

#     print("Este es el hijo1Plano ",hijo1Plano)
#     print("len(padre1Plano)",len(padre1Plano))
#     print("len(padre1Plano+1)",len(padre1Plano)+1)
        
    for unidadP2H2 in padre2Plano[0:corte1P2]:
        hijo2Plano += unidadP2H2
    
    for unidadP1H2 in padre1Plano[corte1P1:corte2P1+1]:
        hijo2Plano += unidadP1H2

    for unidadP2AH2 in padre2Plano[corte2P2+1: len(padre2Plano)]:
        hijo2Plano += unidadP2AH2

    #print("Este es el hijo2Plano ",hijo2Plano)
    
#     Creacion de hijos en forma de lista.
    
    indice1 = 0
    indice2 = longRegla
    hijo1 = []
    for i in range(int((len(hijo1Plano)/longRegla))):
        hijo1.append(hijo1Plano[indice1:indice2])
        indice1 = indice2
        indice2 += longRegla
    #print("este es el hijo1 ya terminado:", hijo1)
    
    indice3 = 0
    indice4 = longRegla
    hijo2 = []
    for i in range(int((len(hijo2Plano)/longRegla))):
        hijo2.append(hijo2Plano[indice3:indice4])
        indice3 = indice4
        indice4 += longRegla
    #print("este es el hijo2 ya terminado:", hijo2)
    print("Resultado de aplicar el metodo de cruce entre padre1:",padre1,"y padre2:",padre2,":")
    return hijo1, hijo2
     

resultadoDelMetodoCombinarParesDePadres = combinarParesPadres(padre1, padre2)
print("resultadoDelMetodoCombinarParesDePadres: ",resultadoDelMetodoCombinarParesDePadres)





# MUTAR hijos resultantes

def mutacion(hijo1, hijo2, probabilidad):
    numero = random.random()
    if numero < probabilidad:
        reglaMutada = ""
        print(numero)
        print("SI se produce la mutacion")
        posicionHijoElegido = random.randrange(0,2)
        posicionReglaElegida = random.randrange(0,len(resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido]))  
        posicionBitElegido = random.randrange(0,longRegla)  
        print(posicionHijoElegido,posicionReglaElegida,posicionBitElegido)
        if resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido][posicionReglaElegida][posicionBitElegido] == "0":
            #print("es un 0")
            
            reglaMutada =  resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido][posicionReglaElegida][0:posicionBitElegido]
            reglaMutada += "1"
            reglaMutada += resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido][posicionReglaElegida][posicionBitElegido+1:longRegla]
            
            
            resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido].pop(posicionReglaElegida)
            resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido].insert(posicionReglaElegida,reglaMutada)
            return hijo1,hijo2
            
        if resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido][posicionReglaElegida][posicionBitElegido] == "1":
            #print("es un 1")
            
            reglaMutada =  resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido][posicionReglaElegida][0:posicionBitElegido]
            reglaMutada += "0"
            reglaMutada += resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido][posicionReglaElegida][posicionBitElegido+1:longRegla]
            
            
            resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido].pop(posicionReglaElegida)
            resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido].insert(posicionReglaElegida,reglaMutada)
            return hijo1,hijo2
            
    else:
        print("NO se produce la mutacion")
        return hijo1,hijo2
 
 

resultadoDelMetodoMutar = mutacion(resultadoDelMetodoCombinarParesDePadres[0], resultadoDelMetodoCombinarParesDePadres[1], 0.1)
print("Resultado de aplicar el metodo de mutacion :", resultadoDelMetodoMutar) 

    
    
# SELECCIONAR individuos para la siguiente generación




 
#Metodo DECODIFICA (recibira un cromosoma, el mejor de la ultima poblacion)

cromosomaParaDecodificar = ['101111000000000000001000000000100000000010000001101', '101111000000000000001000000000100000000010000001101', '100000000010000000001000111000100000000010000000001']

def decodifica (cromosoma):
    i = 0
    cadenaDecodifica = ""
    while (i < len(cromosoma)):
        j = 0
        pos = 0
        for contenido in nValoresDeCadaAtributo:
            indice = 0
            bool = "false"
            for atributo in range (contenido):
                indice = atributo + j
                #bool = "false"
                if cromosoma[i][indice] == "1":
#                     print("valor de i:", i)
#                     print("1")
#                     print("posicion:", listaDeAtributos[pos][atributo])
                    bool = "true"
                    cadenaDecodifica +=  listaDeAtributos[pos][atributo] + "  v  "
            if bool == "true":
                cadenaDecodifica = cadenaDecodifica[0:len(cadenaDecodifica)-3]
                #print("cadenaAlterada :", cadenaDecodifica)
                cadenaDecodifica += "  ^   "
       
            pos += 1
            j += contenido
        i += 1
        cadenaDecodifica = cadenaDecodifica[0:len(cadenaDecodifica)-4] + "entonces  " + str(listaDeAtributos[len(listaDeAtributos)-1][1])
        cadenaDecodifica += "\n"
    return cadenaDecodifica

            
            
    

print("Funcion decodifica :\n",decodifica(cromosomaParaDecodificar) )
             
    
